Appearance
2795. 并行执行 Promise 以获取独有的结果
ts
type FulfilledObj = {
status: 'fulfilled'
value: string
}
type RejectedObj = {
status: 'rejected'
reason: string
}
type Obj = FulfilledObj | RejectedObj
function promiseAllSettled(functions: Function[]): Promise<Obj[]> {
return new Promise((reslove) => {
const result = Array.from({ length: functions.length })
let endCount = 0
for (let i = 0; i < functions.length; i++) {
functions[i]()
.then((value: string) => (result[i] = { status: 'fulfilled', value }))
.catch((reason: string) => (result[i] = { status: 'rejected', reason }))
.finally(() => {
endCount += 1
if (endCount === functions.length) {
reslove(result as Obj[])
}
})
}
})
}自实现 Promise.allSettled
ES2020 引入了原生的 Promise.allSettled,这里本质上是在手写一个等价实现。
核心机制:计数 + 按索引写入。
result 数组的长度预分配为 functions.length,每个 Promise 的 then/catch 写入自己的索引位置。endCount 计数器在 finally 中自增——因为 finally 无论成功失败都会执行,所以不会漏计数。
当 endCount === functions.length 时,所有 Promise 都已 settled,resolve 整个结果数组。
为什么用 finally 计数
then 和 catch 各自处理成功/失败后写入结果,如果把计数也写在这两个分支里,代码会更分散。finally 正好在 then/catch 之后执行,且无条件执行,适合作为统一计数位置。
与 Promise.all 的关键差异
Promise.all 遇到第一个 reject 就立即短路,其他正在执行中的 Promise 的结果会被丢弃。而 allSettled 等待全部完成,返回每个 Promise 的完整状态(成功或失败)。
这类行为通常出现在“批量请求、部分失败不影响整体”的场景里,比如批量检查多个服务状态时,需要保留所有结果而不是在第一个失败后直接中断。
顺序保证
因为每个 Promise 写入 result[i] 是固定的(对应 functions 中的原始位置),无论 Promise 完成的先后顺序如何,最终结果数组中的顺序始终和 functions 中的顺序一致。
